import { useAuthStore } from "@/store/index.ts";
import type { Token, ResponseData } from "./types.d.ts";
/*
 * @Author: MuQuanyu(MQy)
 * @Date: 2025-04-22 15:57:16
 * @LastEditTime: 2025-10-11 08:53:42
 * @email: muquanyu@outlook.com
 */
import axios from "axios";
axios.defaults.withCredentials = true;
import type {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from "axios";
declare module "axios" {
  interface InternalAxiosRequestConfig {
    skipErrorHandling?: boolean; // 跳过统一错误处理选项
    noRefreshToken?: boolean; // 不触发刷新Token选项
  }
  interface AxiosRequestConfig {
    skipErrorHandling?: boolean; // 跳过统一错误处理选项
    noRefreshToken?: boolean; // 不触发刷新Token选项
  }
}
import { ElMessage } from "element-plus";

class HttpClient {
  private instance: AxiosInstance; // axios 实体
  private isRefreshing = false; // 是否正在刷新 Token
  private requestQueue: ((token: string) => void)[] = []; // 重试请求队列

  constructor(baseURL: string) {
    this.instance = axios.create({
      baseURL,
      timeout: 10000, // 超时时间十秒
    });
    // 初始化拦截器
    this.initInterceptors();
  }

  // 是否提前刷新
  private isPreFresh(): boolean {
    const currentTimestamp = Date.now(); // 现在的时间戳
    const authStore = useAuthStore();
    const tokenTimes = authStore.tokenTimes;
    let endTimeMinusFiveMinutes = 0; //提前五分钟刷新的标识
    // tokenTimes?.expiration 这个是token过期时间的时间戳，开发者可以想办法静态或者动态的获取到这么个东西
    // Date.now() + 60 * 60 * 1000 就是生成 token 后往后一小时的时间戳
    if (tokenTimes) {
      endTimeMinusFiveMinutes = tokenTimes?.expiration - 5 * 60 * 1000;
      console.log("endTimeMinusFiveMinutes", endTimeMinusFiveMinutes, currentTimestamp);
      // endTimeMinusFiveMinutes = token?.tokenTimes.expiration - 5 * 1000
    } else {
      return false;
    }

    return endTimeMinusFiveMinutes < currentTimestamp;
  }

  // 初始化拦截器
  private initInterceptors(): void {
    // 请求拦截器
    this.instance.interceptors.request.use(
      async (config: InternalAxiosRequestConfig) => {
        const authStore = useAuthStore();
        const accessToken = authStore.accessToken; // 拿到 accessToken
        const refreshToken: any = authStore.refreshToken; // 拿到 accessToken
        if (accessToken && !this.isRefreshing) {
          if (this.isPreFresh() && !config.noRefreshToken) {
            // 如果需要提前刷新
            try {
              this.isRefreshing = true; // 设为正在刷新 Token
              const newToken = await this.refreshToken(accessToken, refreshToken);
              if (newToken) {
                // 更新token相关
                authStore.setAccessToken(newToken.accessToken);
                authStore.setRefreshToken(newToken.refreshToken);
                authStore.setTokenTimes(newToken.tokenTimes);
                config.headers.Authorization = `Bearer ${newToken.accessToken}`; // 赋予新 accessToken
              } else {
                console.error("Error refreshing token");
                authStore.clearToken(); // 清除 token
              }
            } catch (error) {
              console.error("Error refreshing token:", error);
              config.headers.Authorization = `Bearer ${accessToken}`;
              authStore.clearToken(); // 清除 token
            } finally {
              this.isRefreshing = false; // 结束刷新状态
            }
          } else {
            config.headers.Authorization = `Bearer ${accessToken}`;
          }
        }

        return config;
      },
      (error) => {
        Promise.reject(error);
      }
    );

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse<ResponseData>) => {
        const { code, msg, data } = response.data;

        if (code === 200) return response.data as any;

        // 检查是否需要跳过统一错误处理
        if (response.config.skipErrorHandling) {
          // 直接返回原始响应数据，不进行任何处理
          return response.data;
        }

        // 特殊处理 Token 过期 (假设 code 401 表示 Token 过期)
        if (code === 401) {
          return this.handleTokenExpired(response.config);
        }

        // 其他错误统一提示
        if (!response.config.skipErrorHandling) {
          ElMessage.error(msg + "：" + data || "请求失败");
        }

        return Promise.reject(new Error(msg));
      },
      (error) => {
        const { config, response } = error;
        if (response.status === 401) {
          return this.handleTokenExpired(response.config);
        }

        // 网络错误或服务器异常
        if (!config?.skipErrorHandling) {
          ElMessage.error(error.message || "网络错误");
        }

        return Promise.reject(error);
      }
    );
  }

  // 刷新 token
  private async refreshToken(accessToken: string, refreshToken: string): Promise<Token | null> {
    const res: any = await this.instance.post<ResponseData<Token>>(
      "/api/sys-user/refresh-token",
      {},
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
          "X-Refresh-Token": `Bearer ${refreshToken}`,
        },
        skipErrorHandling: true, // 避免重复提示错误
      }
    );
    if (res.code == 200) {
      return res.data;
    } else {
      const authStore = useAuthStore();
      authStore.clearToken();
      return null;
    }
  }

  // Token 过期处理
  private async handleTokenExpired(config: AxiosRequestConfig): Promise<any> {
    if (!this.isRefreshing) {
      const authStore = useAuthStore();
      try {
        const accessToken: any = authStore.accessToken;
        const refreshToken = authStore.refreshToken;
        if (!refreshToken) {
          // 如果没有 refreshToken 那么无法刷新
          authStore.clearToken();
          ElMessage.error("身份认证过期，请重新登录！");
          throw new Error("请重新登录");
        }
        // 刷新token
        const newToken: Token | null = await this.refreshToken(accessToken, refreshToken);
        console.log("newToken", newToken);
        if (newToken) {
          // 更新token相关
          authStore.setAccessToken(newToken.accessToken);
          authStore.setRefreshToken(newToken.refreshToken);
          authStore.setTokenTimes(newToken.tokenTimes);
          // 重新把旧的请求全部按照新 token 请求一遍
          this.requestQueue.forEach((cb) => cb(newToken.accessToken));
          this.requestQueue = [];
          this.isRefreshing = false;
          // 重新发起当前请求
          return this.instance({
            ...config,
            headers: {
              ...config.headers,
              Authorization: `Bearer ${newToken.accessToken}`,
            },
          });
        } else {
          authStore.clearToken(); // 清除本地 token
          ElMessage.error("刷新 Token 失败，请重新登录"); // 提示用户
          return Promise.reject(new Error("刷新 Token 失败，请重新登录"));
        }
      } catch (error) {
        authStore.clearToken();
        // window.location.href = "/login"; // 跳转到登录页
        return Promise.reject(error);
      }
    }

    // 如果是正在刷新 Token，那你就得把请求加入到 队列里了
    return new Promise((resolve) => {
      this.requestQueue.push((newAccessToken: string) => {
        config.headers!.Authorization = `Bearer ${newAccessToken}`;
        resolve(this.instance(config));
      });
    });
  }

  // 对外暴露的便捷方法

  public request<T = any>(config: AxiosRequestConfig): Promise<ResponseData<T>> {
    return this.instance(config);
  }

  public get<T = any, P = any>(
    url: string,
    params?: P, // 独立的 params 参数
    config?: Omit<AxiosRequestConfig, "params">
  ): Promise<ResponseData<T>> {
    return this.instance.get<T, ResponseData<T>>(url, { ...config, params });
  }

  public post<T = any, D = any>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig
  ): Promise<ResponseData<T>> {
    return this.instance.post<T, ResponseData<T>, D>(url, data, config);
  }
}

// 创建出HTTP示例并且导出
export const http = new HttpClient(import.meta.env.VITE_API_BASE_URL); // 根路径在 .env.development、.env.production 下

/**
 * 之所以单独存储 accessToken、refreshToken、TokenTimes 是因为方便开发者进行二开
 */
